Skip to content

feat(desktop): ChatV3Pane — flagged chat pane on the new spine - #6207

Merged
saddlepaddle merged 4 commits into
mainfrom
chat-v3-pane
Aug 5, 2026
Merged

saddlepaddle merged 4 commits into
mainfrom
chat-v3-pane

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator

Chunk C of plans/chat-v3-pane-mount.md, stacked on #6205. New pane kind "chat-v3" (old pane untouched — swap is the flag-flip PR), registered only when the chat-v3 PostHog flag is on. Full §3 tree: anchor-user-turn transcript (content-visibility offscreen, zero streaming scroll writes except the anchor + approval force-scroll), turn-group collapsing with user-override-wins, toolKind-dispatched rows + open-union fallback, per-block memoized MarkdownView with fence-gated highlighting (Streamdown per block, never whole-message), plain-textarea composer with echo-confirmed draft clearing, harness picker + separate session-status/connection indicators. ai-elements reused per the four never-reuse rules. Wiring: workspace-client host URL, tRPC at /chat-v3/trpc (type-only ChatRouter via tsconfig paths — no runtime dep), WS with per-reconnect token per wsAuth. 117 desktop tests green.

Known merge-time reconciliations: adopt FEATURE_FLAGS.CHAT_V3 from #6203's shared constant (this branch couldn't see it); CDP checklist from the build report covers relay affinity (primeRelayAffinity), real line-diff rendering (follow-up), scroll/perf under live streaming.

https://claude.ai/code/session_01XRFCEPsfmJHu2SenDKYJ2h


Summary by cubic

Adds a new desktop pane for the chat v3 experience, gated by the PostHog chat-v3 flag (via FEATURE_FLAGS.CHAT_V3). Ships transcript/composer/session wiring to the new chat runtime and fixes draft persistence by registering the composer draft key.

  • New Features

    • Registers a new "chat-v3" pane only when the chat-v3 flag is enabled.
    • Transcript: grouped turns with offscreen content-visibility, anchor-to-latest user turn, and auto-scroll to first pending approval.
    • Rich rows: per-block streaming Markdown with fence-aware rendering, reasoning toggle, tool calls (diff/terminal/text), approvals (options + decisions), plans, and notices.
    • Composer: plain textarea with Enter-to-send, debounced local draft persisted under chat-v3-draft:* (key registered), echo-based draft clearing; cancel button while a turn is running.
    • Session UX: header badges for harness and live connection status, session picker listing workspace sessions, and new-session flow with harness selection.
    • Wiring: tRPC client to /chat-v3/trpc, WS streaming under /chat-v3 with token from the workspace client on reconnect.
    • useChatSession.sendPrompt now returns the created OutboxEntry used for echo-based draft clearing.
    • Tests: update subscribeToSession to use scope_id after the rename.
  • Migration

    • Update callers of useChatSession.sendPrompt to handle the returned OutboxEntry (was void).
    • Ensure the host exposes /chat-v3/trpc and WS under /chat-v3, and enable the chat-v3 PostHog flag for rollout.
    • New dependency: @superset/chat (and tsconfig path for @superset/chat-runtime).

Written for commit 0d3bcd0. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added a feature-flagged Chat experience for desktop workspaces.
    • Users can create and switch between sessions using Claude Code or Codex.
    • Added message composition with saved drafts, queued-send handling, cancellation, and retry options.
    • Added rich transcript rendering for Markdown, plans, reasoning, tool calls, diffs, terminal output, notices, and approvals.
    • Added connection and session status indicators, older-message loading, and automatic transcript scrolling.
  • Tests
    • Added coverage for streaming Markdown handling and transcript item classification.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a feature-flagged Chat v3 pane for desktop workspaces. It includes session wiring, harness selection, draft persistence, streaming Markdown, transcript rendering, approvals, tool output, pagination, retries, and outbox handling.

Changes

Chat v3 desktop experience

Layer / File(s) Summary
Runtime contracts and session wiring
apps/desktop/package.json, apps/desktop/tsconfig.json, packages/chat/..., .../ChatV3Pane/hooks/useSessionClient/*
Adds chat dependencies and aliases, returns enqueued outbox entries from sendPrompt, and creates authenticated transport and session clients.
Feature-flagged pane registration
.../v2-workspace/$workspaceId/types.ts, .../usePaneRegistry/*, .../ChatV3Pane/*
Adds Chat v3 pane state, registers the pane when CHAT_V3 is enabled, creates sessions, and switches between new-session and active-session views.
Streaming Markdown rendering
.../MarkdownView/*
Adds stable-block planning, fenced-tail handling, streaming Markdown rendering, and tests for incremental input.
Session start and message composition
.../Composer/*, .../NewSessionView/*, .../SessionHeader/*, .../SessionPicker/*, .../SessionView/*, .../persisted-key-registry.test-data.ts
Adds harness selection, session controls, connection status, workspace-scoped draft persistence, prompt submission, cancellation, and pending-send reconciliation.
Transcript and protocol item rendering
.../Transcript/*
Adds grouped transcript rendering for messages, reasoning, plans, approvals, notices, tool calls, diffs, terminals, text, queued prompts, turn states, and unknown items.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PaneRegistry
  participant ChatV3Pane
  participant ChatTransport
  participant SessionClient
  participant SessionView
  PaneRegistry->>ChatV3Pane: register and render workspace pane
  ChatV3Pane->>ChatTransport: create session with workspace and harness
  ChatV3Pane->>SessionClient: create client for sessionId
  SessionClient->>SessionView: provide session state and stream updates
  SessionView->>ChatTransport: send prompts and approval decisions
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses conventional commit format and clearly summarizes the new feature-flagged ChatV3 desktop pane.
Description check ✅ Passed The description explains the changes, testing status, migration needs, and rollout requirements, but omits the template checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chat-v3-pane

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a feature-flagged Chat V3 desktop pane backed by the shared chat client, including session creation and selection, transcript rendering, approvals, tool output, streaming Markdown, and draft persistence.

  • Registers the new pane and wires host-service tRPC and WebSocket transports.
  • Adds session, transcript, composer, approval, and tool-content UI.
  • Extends useChatSession so sendPrompt returns its outbox entry for echo-confirmed draft clearing.
  • Introduces blocking failure paths around first-prompt creation, initialization errors, and localStorage persistence.

Confidence Score: 4/5

The PR should not merge until failed first-message creation, failed session initialization, and the unbounded unregistered draft persistence are corrected.

A transient host-service or query failure can erase a user's first prompt or leave the pane permanently loading, while the new per-session draft keys violate the desktop persistence lifecycle and guardrail.

Files Needing Attention: ChatV3Pane.tsx, NewSessionView.tsx, SessionView.tsx, Composer.tsx, and packages/chat/src/react/useChatSession/useChatSession.ts

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx Coordinates session creation and first-prompt forwarding, but does not preserve or report the prompt when asynchronous creation fails.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx Implements echo-confirmed clearing for established sessions, while its dynamic localStorage persistence lacks required bounds and cleanup.
packages/chat/src/react/useChatSession/useChatSession.ts Returns outbox entries from sendPrompt, but initialization rejection leaves consumers permanently loading.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx Adds anchored scrolling, collapse overrides, approval targeting, pagination, and retryable outbox rendering without an accepted defect.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts Wires host-service tRPC and reconnect-token WebSockets and closes clients when their identity changes.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx Registers the chat-v3 pane only while its PostHog feature flag is enabled.

Sequence Diagram

sequenceDiagram
  participant User
  participant Pane as ChatV3Pane
  participant Host as chat-v3 tRPC
  participant Hook as useChatSession
  participant Stream as WebSocket stream
  User->>Pane: Submit first prompt
  Pane->>Host: createSession
  Host-->>Pane: sessionId
  Pane->>Hook: Mount SessionView
  Hook->>Host: getSession + getItems
  Hook->>Stream: subscribe from cursor
  Stream-->>Hook: envelopes and deltas
  Hook-->>Pane: snapshot, outbox, status
Loading

Comments Outside Diff (1)

  1. packages/chat/src/react/useChatSession/useChatSession.ts, line 161-183 (link)

    P1 Seed failure leaves loading stuck

    When getSession or getItems rejects while the host service is unavailable, the discarded seed() promise never transitions the hook out of loading, leaving the pane on an indefinite spinner with its composer disabled and no retry or error state.

    Knowledge Base Used: Desktop App (Electron main + renderer)

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/chat/src/react/useChatSession/useChatSession.ts
    Line: 161-183
    
    Comment:
    **Seed failure leaves loading stuck**
    
    When `getSession` or `getItems` rejects while the host service is unavailable, the discarded `seed()` promise never transitions the hook out of `loading`, leaving the pane on an indefinite spinner with its composer disabled and no retry or error state.
    
    **Knowledge Base Used:** [Desktop App (Electron main + renderer)](https://app.greptile.com/personal-org-6810/-/custom-context/knowledge-base/superset-sh/superset/-/docs/desktop-app.md)
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:47-50
**First prompt clears before creation**

When session creation rejects because the host service is starting or unavailable, this callback has already returned `null`, causing `Composer` to erase the persisted first prompt even though no session or recoverable outbox entry was created.

### Issue 2
packages/chat/src/react/useChatSession/useChatSession.ts:161-183
**Seed failure leaves loading stuck**

When `getSession` or `getItems` rejects while the host service is unavailable, the discarded `seed()` promise never transitions the hook out of `loading`, leaving the pane on an indefinite spinner with its composer disabled and no retry or error state.

### Issue 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx:74-76
**Session drafts grow without cleanup**

Each session creates a distinct `chat-v3-draft:${sessionId}` localStorage key, but these writers are not registered, bounded, or reconciled when sessions are deleted. Abandoned drafts therefore accumulate indefinitely in the renderer's shared synchronous localStorage quota and violate the persisted-key guardrail.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(desktop): ChatV3Pane behind chat-v3..." | Re-trigger Greptile

Comment on lines +47 to +50
onSend={(content) => {
onSend(content);
return null;
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 First prompt clears before creation

When session creation rejects because the host service is starting or unavailable, this callback has already returned null, causing Composer to erase the persisted first prompt even though no session or recoverable outbox entry was created.

Knowledge Base Used: Desktop App (Electron main + renderer)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
Line: 47-50

Comment:
**First prompt clears before creation**

When session creation rejects because the host service is starting or unavailable, this callback has already returned `null`, causing `Composer` to erase the persisted first prompt even though no session or recoverable outbox entry was created.

**Knowledge Base Used:** [Desktop App (Electron main + renderer)](https://app.greptile.com/personal-org-6810/-/custom-context/knowledge-base/superset-sh/superset/-/docs/desktop-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +74 to +76
<Composer
disabled={session.status !== "ready"}
draftKey={`chat-v3-draft:${sessionId}`}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Session drafts grow without cleanup

Each session creates a distinct chat-v3-draft:${sessionId} localStorage key, but these writers are not registered, bounded, or reconciled when sessions are deleted. Abandoned drafts therefore accumulate indefinitely in the renderer's shared synchronous localStorage quota and violate the persisted-key guardrail.

Knowledge Base Used: Desktop App (Electron main + renderer)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx
Line: 74-76

Comment:
**Session drafts grow without cleanup**

Each session creates a distinct `chat-v3-draft:${sessionId}` localStorage key, but these writers are not registered, bounded, or reconciled when sessions are deleted. Abandoned drafts therefore accumulate indefinitely in the renderer's shared synchronous localStorage quota and violate the persisted-key guardrail.

**Knowledge Base Used:** [Desktop App (Electron main + renderer)](https://app.greptile.com/personal-org-6810/-/custom-context/knowledge-base/superset-sh/superset/-/docs/desktop-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

16 issues found across 56 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts:26">
P1: ChatV3 cannot create/list sessions or receive live updates because the URLs wired here are not served by host-service. Register the chat-v3 tRPC and WebSocket routes (with authentication) before targeting these endpoints.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx:20">
P2: All tool calls render through the same generic card, so the pane ignores the protocol’s `toolKind`-specific rendering behavior for read/edit/execute/fetch/other calls. Dispatch the row on `item.toolKind` and keep a generic fallback for unsupported kinds.</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx:24">
P3: Hovering an expanded tool card shows the closed-direction chevron because this `ToolHeader` call does not provide the current open state. Passing the actual collapsible state or making the shared header read its parent state would keep the expansion affordance consistent.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx:3">
P2: Tool text content is effectively unbounded in the protocol (z.string(), no truncation), and unlike the sibling TerminalContent (20k-char clamp + max-h-80) this <pre> renders it fully with no height cap, so a large tool output (e.g. read file) can mount one huge DOM node — a real perf risk given this pane's deliberate streaming/scroll optimizations. Consider clamping character count and adding a max-height with vertical overflow, matching the terminal's convention.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx:39">
P3: The first-prompt effect reruns on every session render, including each live-stream snapshot update, because it depends on the freshly-created `session` object rather than the fields it reads. Narrow the dependencies to `session.status` and `session.sendPrompt` (plus the actual props) so streaming does not repeatedly schedule this effect.</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx:55">
P2: A failed initial session request leaves the pane showing the loading spinner forever because this view has no error/retry branch and the hook never transitions out of `"loading"` on seed failure. Expose a load-error state with retry or render an actionable failure here.</violation>

<violation number="3" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx:65">
P2: Loading older messages, answering an approval, or cancelling a turn can produce an unhandled promise rejection, with no user-facing failure state, when the transport request fails. Catch these action promises and surface or log the failure through the pane’s error handling.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/SessionHeader.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/SessionHeader.tsx:36">
P2: Long session titles can make the header overflow and hide the status or connection indicator because the picker trigger is untruncated and the header adds several non-shrinking items; constraining the picker with a `min-w-0`/ellipsis treatment while reserving space for the indicators would keep the header usable.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx:90">
P2: Expanding or collapsing a tool run can affect a different run after older messages are loaded because this key is position-based rather than tied to the run’s items. A stable key derived from the tool IDs, shared with `Transcript.expandAll`, would preserve each user override across timeline updates.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:32">
P3: Screen-reader users cannot tell which harness will be used for the next session because selection is conveyed only through the visual button variant. Add `aria-pressed={candidate === harness}` to each harness button.</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:49">
P1: A failed new-session request clears the user's draft before the session exists. The send path should retain the draft until session creation and first-prompt delivery are acknowledged, rather than adapting this asynchronous operation to Composer's immediate-success `null` result.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx:39">
P2: Abandoned chat drafts can grow renderer localStorage without a size bound or cleanup path: each session/workspace gets a separate key and unsent drafts survive indefinitely. Add a bounded/expiring reconciliation strategy or move entity-scoped draft persistence to the supported SQLite layer.</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx:39">
P2: The desktop persisted-key registry check will fail for this new `localStorage` writer because the Composer file is not registered. Add the `chat-v3-draft:*` key family to the registry with its bounds and deletion policy.</violation>

<violation number="3" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx:48">
P1: Switching sessions before a sent message is echoed can resurrect that message as a draft: the unmount cleanup saves the pending sent text, but the pending outbox/echo state is destroyed with the old session client. Make cleanup/send state durable across the session switch or remove/mark the draft when dispatching so an accepted prompt cannot be duplicated.</violation>

<violation number="4" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx:95">
P2: The send and cancel controls are unlabeled icon-only buttons, so assistive-technology users cannot identify their actions. Add explicit labels such as `Send message` and `Cancel turn`.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx:144">
P2: A newly queued or failed prompt can be rendered below the visible transcript without being brought into view; in particular, a failed send can leave its Retry and Discard controls inaccessible unless the user manually scrolls. Scrolling only newly added outbox rows into view would preserve the no-per-token streaming-scroll behavior while keeping pending failures actionable.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Renderer as Desktop Renderer
    participant Registry as usePaneRegistry
    participant PostHog as PostHog Flags
    participant Pane as ChatV3Pane
    participant WsClient as WorkspaceClient
    participant Session as SessionClient (@superset/chat)
    participant Runtime as Chat Host (/chat-v3)
    participant Composer as Composer
    participant Transcript as Transcript + MarkdownView

    Renderer->>Registry: render panes for workspace
    Registry->>PostHog: NEW: read FEATURE_FLAGS.CHAT_V3 via useFeatureFlagEnabled
    alt CHAT_V3 flag enabled
        Registry->>Renderer: NEW: register "chat-v3" pane (legacy chat pane untouched)
    else CHAT_V3 flag disabled
        Registry-->>Renderer: chat-v3 pane not registered
    end

    Renderer->>Pane: mount ChatV3Pane(sessionId, workspaceId)
    Pane->>WsClient: read hostUrl + getWsToken()
    Note over Pane,Session: NEW: tRPC httpBatchLink to /chat-v3/trpc (type-only ChatRouter, host-service headers)
    Pane->>Session: create SessionClient with transport/stream wiring

    opt no active session
        Composer->>Pane: send first prompt (UserContent)
        Pane->>Runtime: tRPC createSession(workspaceId, harness)
        Runtime-->>Pane: sessionId
        Pane->>Session: NEW: create client for sessionId with pendingFirstPrompt
    end

    Session->>Runtime: NEW: open WebSocket /chat-v3 (per-reconnect token from getWsToken)
    Session->>Runtime: tRPC prompt (flush pending first prompt)
    Runtime-->>Session: stream session events (items, turn state, approvals)
    Session-->>Transcript: snapshot with timeline groups + approvals + outbox
    Note over Session,Transcript: SessionHeader mirrors WS connection and session status badges

    Transcript->>Transcript: CHANGED: render grouped turns (content-visibility offscreen, anchor to latest user turn)
    alt streaming tail has open fence
        Transcript->>Transcript: render tail as plain pre until fence closes
    else tail is closed markdown
        Transcript->>Transcript: NEW: per-block memoized Streamdown (never whole message)
    end

    User->>Composer: enter prompt
    Composer->>Session: sendPrompt(content)
    Session-->>Composer: CHANGED: returns OutboxEntry(clientId)
    alt outbox flush succeeds
        Session->>Runtime: tRPC prompt (flush outbox)
        Runtime-->>Session: stream echo confirms outbox entry removal
        Session-->>Composer: outbox entry gone
        Composer->>Composer: clear draft only if text is unchanged (echo-confirmed)
    else outbox flush fails
        Session-->>Composer: entry marked failed
        Composer->>Session: retryPrompt(clientId) / discardPrompt(clientId)
    end

    User->>Transcript: respond to pending approval
    Transcript->>Session: respondToApproval(approvalId, decision)
    Session->>Runtime: tRPC respondToApproval(...)
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const client = createTRPCClient<ChatRouter>({
links: [
httpBatchLink({
url: `${hostUrl}/chat-v3/trpc`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: ChatV3 cannot create/list sessions or receive live updates because the URLs wired here are not served by host-service. Register the chat-v3 tRPC and WebSocket routes (with authentication) before targeting these endpoints.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts, line 26:

<comment>ChatV3 cannot create/list sessions or receive live updates because the URLs wired here are not served by host-service. Register the chat-v3 tRPC and WebSocket routes (with authentication) before targeting these endpoints.</comment>

<file context>
@@ -0,0 +1,77 @@
+		const client = createTRPCClient<ChatRouter>({
+			links: [
+				httpBatchLink({
+					url: `${hostUrl}/chat-v3/trpc`,
+					headers: () => getHostServiceHeaders(hostUrl),
+				}),
</file context>

draftKey={`chat-v3-draft:new:${workspaceId}`}
onSend={(content) => {
onSend(content);
return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A failed new-session request clears the user's draft before the session exists. The send path should retain the draft until session creation and first-prompt delivery are acknowledged, rather than adapting this asynchronous operation to Composer's immediate-success null result.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx, line 49:

<comment>A failed new-session request clears the user's draft before the session exists. The send path should retain the draft until session creation and first-prompt delivery are acknowledged, rather than adapting this asynchronous operation to Composer's immediate-success `null` result.</comment>

<file context>
@@ -0,0 +1,56 @@
+				draftKey={`chat-v3-draft:new:${workspaceId}`}
+				onSend={(content) => {
+					onSend(content);
+					return null;
+				}}
+				outbox={[]}
</file context>

return () => {
const latest = valueRef.current;
if (latest === "") window.localStorage.removeItem(draftKey);
else window.localStorage.setItem(draftKey, latest);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Switching sessions before a sent message is echoed can resurrect that message as a draft: the unmount cleanup saves the pending sent text, but the pending outbox/echo state is destroyed with the old session client. Make cleanup/send state durable across the session switch or remove/mark the draft when dispatching so an accepted prompt cannot be duplicated.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx, line 48:

<comment>Switching sessions before a sent message is echoed can resurrect that message as a draft: the unmount cleanup saves the pending sent text, but the pending outbox/echo state is destroyed with the old session client. Make cleanup/send state durable across the session switch or remove/mark the draft when dispatching so an accepted prompt cannot be duplicated.</comment>

<file context>
@@ -0,0 +1,109 @@
+		return () => {
+			const latest = valueRef.current;
+			if (latest === "") window.localStorage.removeItem(draftKey);
+			else window.localStorage.setItem(draftKey, latest);
+		};
+	}, [draftKey]);
</file context>

return `${((item.completedAtMs - item.startedAtMs) / 1000).toFixed(1)}s`;
}

export function ToolCallRow({ item }: { item: ToolCall }) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: All tool calls render through the same generic card, so the pane ignores the protocol’s toolKind-specific rendering behavior for read/edit/execute/fetch/other calls. Dispatch the row on item.toolKind and keep a generic fallback for unsupported kinds.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx, line 20:

<comment>All tool calls render through the same generic card, so the pane ignores the protocol’s `toolKind`-specific rendering behavior for read/edit/execute/fetch/other calls. Dispatch the row on `item.toolKind` and keep a generic fallback for unsupported kinds.</comment>

<file context>
@@ -0,0 +1,53 @@
+	return `${((item.completedAtMs - item.startedAtMs) / 1000).toFixed(1)}s`;
+}
+
+export function ToolCallRow({ item }: { item: ToolCall }) {
+	const duration = durationLabel(item);
+	return (
</file context>

@@ -0,0 +1,7 @@
export function TextContent({ text }: { text: string }) {
return (
<pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2 font-mono text-xs">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Tool text content is effectively unbounded in the protocol (z.string(), no truncation), and unlike the sibling TerminalContent (20k-char clamp + max-h-80) this

 renders it fully with no height cap, so a large tool output (e.g. read file) can mount one huge DOM node — a real perf risk given this pane's deliberate streaming/scroll optimizations. Consider clamping character count and adding a max-height with vertical overflow, matching the terminal's convention.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx, line 3:

<comment>Tool text content is effectively unbounded in the protocol (z.string(), no truncation), and unlike the sibling TerminalContent (20k-char clamp + max-h-80) this <pre> renders it fully with no height cap, so a large tool output (e.g. read file) can mount one huge DOM node — a real perf risk given this pane's deliberate streaming/scroll optimizations. Consider clamping character count and adding a max-height with vertical overflow, matching the terminal's convention.</comment>

<file context>
@@ -0,0 +1,7 @@
+export function TextContent({ text }: { text: string }) {
+	return (
+		<pre className="overflow-x-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2 font-mono text-xs">
+			{text}
+		</pre>
</file context>

useEffect(() => {
const timer = setTimeout(() => {
if (value === "") window.localStorage.removeItem(draftKey);
else window.localStorage.setItem(draftKey, value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The desktop persisted-key registry check will fail for this new localStorage writer because the Composer file is not registered. Add the chat-v3-draft:* key family to the registry with its bounds and deletion policy.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx, line 39:

<comment>The desktop persisted-key registry check will fail for this new `localStorage` writer because the Composer file is not registered. Add the `chat-v3-draft:*` key family to the registry with its bounds and deletion policy.</comment>

<file context>
@@ -0,0 +1,109 @@
+	useEffect(() => {
+		const timer = setTimeout(() => {
+			if (value === "") window.localStorage.removeItem(draftKey);
+			else window.localStorage.setItem(draftKey, value);
+		}, DRAFT_DEBOUNCE_MS);
+		return () => clearTimeout(timer);
</file context>

snapshot={snapshot}
/>
))}
{outbox.map((entry) => (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A newly queued or failed prompt can be rendered below the visible transcript without being brought into view; in particular, a failed send can leave its Retry and Discard controls inaccessible unless the user manually scrolls. Scrolling only newly added outbox rows into view would preserve the no-per-token streaming-scroll behavior while keeping pending failures actionable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx, line 144:

<comment>A newly queued or failed prompt can be rendered below the visible transcript without being brought into view; in particular, a failed send can leave its Retry and Discard controls inaccessible unless the user manually scrolls. Scrolling only newly added outbox rows into view would preserve the no-per-token streaming-scroll behavior while keeping pending failures actionable.</comment>

<file context>
@@ -0,0 +1,181 @@
+					snapshot={snapshot}
+				/>
+			))}
+			{outbox.map((entry) => (
+				<div
+					className="flex flex-col items-end gap-1 self-end"
</file context>

const duration = durationLabel(item);
return (
<Tool defaultOpen={item.status === "running"}>
<ToolHeader

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Hovering an expanded tool card shows the closed-direction chevron because this ToolHeader call does not provide the current open state. Passing the actual collapsible state or making the shared header read its parent state would keep the expansion affordance consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx, line 24:

<comment>Hovering an expanded tool card shows the closed-direction chevron because this `ToolHeader` call does not provide the current open state. Passing the actual collapsible state or making the shared header read its parent state would keep the expansion affordance consistent.</comment>

<file context>
@@ -0,0 +1,53 @@
+	const duration = durationLabel(item);
+	return (
+		<Tool defaultOpen={item.status === "running"}>
+			<ToolHeader
+				state={STATE_BY_STATUS[item.status]}
+				title={item.title}
</file context>

firstPromptSentRef.current = true;
session.sendPrompt(pendingFirstPrompt);
onFirstPromptSent();
}, [pendingFirstPrompt, session, onFirstPromptSent]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The first-prompt effect reruns on every session render, including each live-stream snapshot update, because it depends on the freshly-created session object rather than the fields it reads. Narrow the dependencies to session.status and session.sendPrompt (plus the actual props) so streaming does not repeatedly schedule this effect.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx, line 39:

<comment>The first-prompt effect reruns on every session render, including each live-stream snapshot update, because it depends on the freshly-created `session` object rather than the fields it reads. Narrow the dependencies to `session.status` and `session.sendPrompt` (plus the actual props) so streaming does not repeatedly schedule this effect.</comment>

<file context>
@@ -0,0 +1,85 @@
+		firstPromptSentRef.current = true;
+		session.sendPrompt(pendingFirstPrompt);
+		onFirstPromptSent();
+	}, [pendingFirstPrompt, session, onFirstPromptSent]);
+
+	const runningTurnId = useMemo(() => {
</file context>

key={candidate}
onClick={() => onHarnessChange(candidate)}
size="sm"
variant={candidate === harness ? "secondary" : "ghost"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Screen-reader users cannot tell which harness will be used for the next session because selection is conveyed only through the visual button variant. Add aria-pressed={candidate === harness} to each harness button.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx, line 32:

<comment>Screen-reader users cannot tell which harness will be used for the next session because selection is conveyed only through the visual button variant. Add `aria-pressed={candidate === harness}` to each harness button.</comment>

<file context>
@@ -0,0 +1,56 @@
+							key={candidate}
+							onClick={() => onHarnessChange(candidate)}
+							size="sm"
+							variant={candidate === harness ? "secondary" : "ghost"}
+						>
+							{candidate}
</file context>

… session wiring

New pane kind "chat-v3" (the "chat" registry swap stays for the
flag-flip PR), gated by the chat-v3 PostHog flag. Transcript renders
timeline turn groups with content-visibility offscreen containment,
anchor-user-turn scrolling and approval scroll-into-view — no bottom
chasing; MarkdownView is per-block memoized over splitBlocks with plain
pre while a fence is open; toolKind-dispatched rows fall back to
UnknownItemRow; Composer persists drafts debounced and clears only on
clientId echo. Wiring: package-owned tRPC client at hostUrl/chat-v3/trpc
typed off ChatRouter (type-only via tsconfig paths), WS stream via
buildStreamUrl + token query param from the workspace client.
useChatSession.sendPrompt now returns the outbox entry so the composer
can watch for its echo.

Claude-Session: https://claude.ai/code/session_01XRFCEPsfmJHu2SenDKYJ2h

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (3)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx (1)

32-39: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Narrow the effect dependencies to the values it reads.

useChatSession returns a new object on every render, so session changes identity each render and this effect re-runs each render. firstPromptSentRef keeps the behavior correct, but the extra runs are avoidable. Depend on session.status and session.sendPrompt instead.

♻️ Proposed refactor
 	const firstPromptSentRef = useRef(false);
+	const sendPrompt = session.sendPrompt;
+	const status = session.status;
 	useEffect(() => {
 		if (!pendingFirstPrompt || firstPromptSentRef.current) return;
-		if (session.status !== "ready") return;
+		if (status !== "ready") return;
 		firstPromptSentRef.current = true;
-		session.sendPrompt(pendingFirstPrompt);
+		sendPrompt(pendingFirstPrompt);
 		onFirstPromptSent();
-	}, [pendingFirstPrompt, session, onFirstPromptSent]);
+	}, [pendingFirstPrompt, status, sendPrompt, onFirstPromptSent]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx
around lines 32 - 39, Update the effect in SessionView to depend on the specific
session values it reads: session.status and session.sendPrompt, rather than the
unstable session object. Keep the existing pendingFirstPrompt,
onFirstPromptSent, readiness check, ref guard, and prompt-sending behavior
unchanged.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx (1)

26-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Map harness ids to display labels and expose the selected state.

The buttons render the raw ids claude-code and codex. Add a label map so the UI shows product names. Add aria-pressed so assistive technology reports which harness is selected. The variant change alone conveys selection only visually.

♻️ Proposed refactor
+const HARNESS_LABELS: Record<HarnessId, string> = {
+	"claude-code": "Claude Code",
+	codex: "Codex",
+};
+
 				<div className="ml-auto flex items-center gap-1">
 					{HARNESSES.map((candidate) => (
 						<Button
+							aria-pressed={candidate === harness}
 							key={candidate}
 							onClick={() => onHarnessChange(candidate)}
 							size="sm"
 							variant={candidate === harness ? "secondary" : "ghost"}
 						>
-							{candidate}
+							{HARNESS_LABELS[candidate]}
 						</Button>
 					))}
 				</div>

Line 42 and line 52 also interpolate the raw id into prose. Use the label map there too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
around lines 26 - 37, Update NewSessionView’s harness rendering to use a label
map for HARNESSES, displaying product names instead of raw ids in the buttons
and the prose at lines 42 and 52. Add aria-pressed to each harness Button based
on whether candidate matches harness, while preserving the existing variant and
onHarnessChange behavior.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx (1)

32-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move ItemRow into its own component folder.

This file defines both ItemRow and TurnGroupSection. Put ItemRow in ItemRow/ItemRow.tsx with an index.ts barrel export, or inline its switch into TurnGroupSection.

As per coding guidelines, “Use one folder per component” and “Use one component per file.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx
around lines 32 - 60, Separate the ItemRow component from TurnGroupSection by
moving the ItemRow function and its related imports into an ItemRow/ItemRow.tsx
component file, then add an index.ts barrel export and update TurnGroupSection
imports accordingly. Preserve the existing rowKindForItem guard and item-kind
switch behavior unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx:
- Around line 24-32: Update the createSession callback to track whether session
creation is pending, return early while a creation is in progress, and serialize
rapid submissions. Handle rejected wiring.transport.createSession calls within
the callback, and only update pending prompt and session ID state after
successful creation; clear the pending state on both success and failure.
- Around line 39-41: Update the ChatV3Pane session/prompt state so
pendingFirstPrompt stores both the created sessionId and prompt content. In
SessionView, pass the pending content only when its stored sessionId matches the
active sessionId; clear the pending prompt when starting or selecting another
session, while preserving normal prompt handling.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx:
- Around line 27-34: Replace the unbounded session-scoped localStorage
persistence around the Composer draft state and draftKey with
createElectronSQLitePersistence, or add a bounded cleanup mechanism for
chat-v3-draft:* keys that removes abandoned session drafts. Document the key
family’s bound, deletion owner/path, and feature-removal behavior near the
relevant registry or persistence declaration.
- Around line 94-106: Add accessible names to both icon-only Buttons in the
Composer component: provide a clear cancel label for the button using
onCancelTurn and a clear send label for the button using send, while preserving
their existing handlers, disabled state, variants, and icons.
- Around line 52-61: Update the pending-send handling in Composer’s useEffect so
the textarea and draft localStorage are cleared only after the tracked clientId
is confirmed as successfully sent, not merely removed from outbox. Distinguish
confirmation from discard using the existing entry status or a
clear-only-on-success signal, while preserving the valueRef check and pendingRef
cleanup.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/MarkdownView.tsx:
- Around line 6-20: Move the memoized MarkdownBlock component into a new
MarkdownBlock/MarkdownBlock.tsx file, preserving its props, Streamdown
configuration, and rendering behavior. Add an index.ts barrel export for
MarkdownBlock, then update MarkdownView to import and use the extracted
component instead of defining it locally.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:
- Around line 45-53: Update the NewSessionView Composer onSend flow and its
ChatV3Pane createSession callback to return a success boolean, resolving true
only after session creation succeeds and false on failure. Have Composer.send
await this result and clear the textarea and draft key only when it is true; on
failure, preserve the draft and render a user-visible error with select-text
cursor-text classes, avoiding an unhandled rejection.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/TerminalContent.tsx:
- Around line 22-24: Update the <pre> element in TerminalContent to include the
select-text and cursor-text classes alongside its existing classes, allowing
users to select and copy persistent terminal output.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx:
- Around line 127-130: Make all persistent error messages selectable by adding
the select-text cursor-text classes to the failed-turn message in
TurnGroupSection.tsx:127-130, the outbox-failed rendering in
Transcript.tsx:153-157, and the error notice rendering in NoticeRow.tsx:4-15;
leave non-error states unchanged.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.test.ts:
- Around line 9-40: Update the rowKind test cases to import and use the RowKind
type, changing cases to Array<[Item, RowKind]>. Remove the expected as never
cast and pass expected directly to toBe so the compiler validates each expected
row kind.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:
- Around line 26-37: Update NewSessionView’s harness rendering to use a label
map for HARNESSES, displaying product names instead of raw ids in the buttons
and the prose at lines 42 and 52. Add aria-pressed to each harness Button based
on whether candidate matches harness, while preserving the existing variant and
onHarnessChange behavior.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx:
- Around line 32-39: Update the effect in SessionView to depend on the specific
session values it reads: session.status and session.sendPrompt, rather than the
unstable session object. Keep the existing pendingFirstPrompt,
onFirstPromptSent, readiness check, ref guard, and prompt-sending behavior
unchanged.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx:
- Around line 32-60: Separate the ItemRow component from TurnGroupSection by
moving the ItemRow function and its related imports into an ItemRow/ItemRow.tsx
component file, then add an index.ts barrel export and update TurnGroupSection
imports accordingly. Preserve the existing rowKindForItem guard and item-kind
switch behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0fa7c212-35e4-4506-90aa-f285f445e9f3

📥 Commits

Reviewing files that changed from the base of the PR and between af10c7b and 0d3bcd0.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (57)
  • apps/desktop/package.json
  • apps/desktop/src/renderer/lib/persisted-keys/persisted-key-registry.test-data.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/MarkdownView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/SessionHeader.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/SessionPicker.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/AgentMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/ApprovalRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/NoticeRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/PlanRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/ReasoningRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/ToolContentList.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/DiffContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/TerminalContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/UnknownItemRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/UserMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types.ts
  • apps/desktop/tsconfig.json
  • packages/chat/src/client/subscribeToSession/subscribeToSession.test.ts
  • packages/chat/src/react/useChatSession/useChatSession.ts

Comment on lines +24 to +32
const createSession = useCallback(
async (content: UserContent[] | null) => {
const created = await wiring.transport.createSession({
commandId: crypto.randomUUID(),
workspaceId,
harness,
});
setPendingFirstPrompt(content);
onSessionIdChange(created.sessionId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Serialize session creation and handle failures.

Each submission creates a new commandId, but this callback has no pending guard. Rapid submissions can create multiple remote sessions. A rejected createSession call is also unhandled because the caller discards its promise. Track creation state, prevent duplicate submission, and catch the failure before updating pane state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx
around lines 24 - 32, Update the createSession callback to track whether session
creation is pending, return early while a creation is in progress, and serialize
rapid submissions. Handle rejected wiring.transport.createSession calls within
the callback, and only update pending prompt and session ID state after
successful creation; clear the pending state on both success and failure.

Comment on lines +39 to +41
activeSessionId={sessionId}
onNewSession={() => onSessionIdChange(null)}
onSelect={onSessionIdChange}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Bind pendingFirstPrompt to its created session.

pendingFirstPrompt has no session ID. If the user selects another session before the created client becomes ready, SessionView sends that prompt to the newly selected session. Store { sessionId, content } and pass the content only when it matches the active session. Clear it when the user starts or selects another session.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx
around lines 39 - 41, Update the ChatV3Pane session/prompt state so
pendingFirstPrompt stores both the created sessionId and prompt content. In
SessionView, pass the pending content only when its stored sessionId matches the
active sessionId; clear the pending prompt when starting or selecting another
session, while preserving normal prompt handling.

Comment on lines +27 to +34
const [value, setValue] = useState(
() => window.localStorage.getItem(draftKey) ?? "",
);
const valueRef = useRef(value);
valueRef.current = value;
const pendingRef = useRef<{ clientId: string; sentText: string } | null>(
null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Bound the per-session draft keys or move them to SQLite.

draftKey is chat-v3-draft:${sessionId} for session views, so each session creates its own localStorage key. This file removes a key only when the user clears or sends that draft in that mounted Composer. A user who types text and abandons the session leaves the key behind permanently. No cap, TTL, or entity reconciliation deletes stranded session drafts, and the registry entry declares the family without a bound or deletion owner.

Add one of the following:

  • an LRU or fixed cap over chat-v3-draft:* keys,
  • a boot sweep that deletes draft keys for sessions that no longer exist,
  • or createElectronSQLitePersistence for session-scoped drafts.

Also document the bound and deletion owner for the key family.

Based on learnings from the coding guidelines: "Use localStorage only for small singleton UI state; entity-scoped unbounded data or payloads larger than a few KB belongs in SQLite via createElectronSQLitePersistence" and "For every renderer localStorage key, document its bound, deletion owner or path, and behavior when the feature is removed; use caps/LRU, TTL, entity reconciliation, or a fixed-size singleton as appropriate."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx
around lines 27 - 34, Replace the unbounded session-scoped localStorage
persistence around the Composer draft state and draftKey with
createElectronSQLitePersistence, or add a bounded cleanup mechanism for
chat-v3-draft:* keys that removes abandoned session drafts. Document the key
family’s bound, deletion owner/path, and feature-removal behavior near the
relevant registry or persistence declaration.

Source: Coding guidelines

Comment on lines +52 to +61
useEffect(() => {
const pending = pendingRef.current;
if (!pending) return;
if (outbox.some((entry) => entry.clientId === pending.clientId)) return;
pendingRef.current = null;
if (valueRef.current === pending.sentText) {
setValue("");
window.localStorage.removeItem(draftKey);
}
}, [outbox, draftKey]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect OutboxEntryState members and discard/confirm transitions.
fd -t f 'outbox.ts' packages/chat/src | xargs rg -n -C3 'OutboxEntryState|state\s*[:=]\s*"|discard|confirm'

Repository: superset-sh/superset

Length of output: 1823


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/\$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx"

echo "== file exists =="
test -f "$FILE" && echo yes || echo no

echo "== composer outline =="
ast-grep outline "$FILE" --view expanded | sed -n '1,220p'

echo "== composer relevant lines =="
cat -n "$FILE" | sed -n '1,140p'

echo "== references to clear/draft/pending text and outbox discard/confirm =="
rg -n -C3 'discards?|discardPrompt|onDiscard|confirm|pendingRef|sentText|setValue|localStorage.removeItem|outbox' apps/desktop/src/renderer packages -g '*.ts' -g '*.tsx' | sed -n '1,240p'

Repository: superset-sh/superset

Length of output: 27039


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate ChatV3Pane files =="
fd -t f 'ChatV3Pane|Transcript|Outbox' apps/desktop/src/renderer packages -i | sed -n '1,200p'

echo "== ChatV3Pane references to discard/confirm/outbox =="
rg -n -C4 'outbox|confirm|discard|onConfirm|onDiscard|onDiscardPrompt' apps/desktop/src/renderer -g '*.ts' -g '*.tsx' | sed -n '1,260p'

echo "== focused app files =="
for name in "ChatV3Pane.tsx" "Transcript.tsx" "ChatV3Pane.test.tsx"; do
  echo "-- $name --"
  fd -t f "$name" apps/desktop/src/renderer -i | while read -r f; do
    echo "FILE: $f"
    rg -n -C4 'outbox|confirm|discard|onConfirm|onDiscard|onDiscardPrompt' "$f" || true
  done
done

echo "== chat package transcript-related references =="
rg -n -C4 'outbox|confirm|discard|pendingRef|sentText|onDiscardPrompt|onConfirm' packages apps -g '*.ts' -g '*.tsx' -g '*.test-data.ts' || true

Repository: superset-sh/superset

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/\$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx"
PANEDIR="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workcase/\$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane"
APPFILES="apps/desktop/src/renderer"

echo "== exact Composer file =="
wc -l "$FILE"
sed -n '1,130p' "$FILE"

echo "== exact ChatV3Pane file candidates =="
fd -i 'ChatV3Pane\.tsx$' "$APPFILES" | while read -r p; do
  echo "---- $p ----"
  rg -n -C5 'outbox|confirm|discard|onConfirm|onDiscard|onDiscardPrompt' "$p" || true
done

echo "== exact Transcript file candidates =="
fd -i 'Transcript\.tsx$' "$APPFILES" | while read -r p; do
  echo "---- $p ----"
  rg -n -C5 'outbox|confirm|discard|onConfirm|onDiscard|onDiscardPrompt' "$p" || true
done

echo "== focused app renderer search exact terms =="
rg -n -w -C3 '\'outbox\\'|\'confirm\\'|\'discard\\'|onConfirm|onDiscard|onDiscardPrompt|outbox' "$APPFILES" -g '*.ts' -g '*.tsx' | sed -n '1,260p'

echo "== focused package chat-outbox exact search =="
find packages -path '*chat*' -type f \( -name '*.ts' -o -name '*.tsx' \) -print0 | xargs -0 rg -n -w -C3 'OutboxEntryState|OutboxEntry|confirm\\(|discard\\(|outbox' | sed -n '1,220p' | rg -n -C5 'packages/chat|outbox|confirm|discard|OutboxEntry' || true

echo "== behavioral probe for current effect logic ==
python3 - <<'PY'
def current_clears(pending, outbox, value, sent_text):
    if not pending:
        return False
    if any(entry == pending for entry in outbox):
        return False
    if value == sent_text:
        return True
    return False

for state, present, edited, failed in [
    ("inflight", True, False, False),
    ("failed", False, False, False),
    ("failed", False, True, False),
    ("queued", False, False, True),
]:
    outbox = [pending := {"clientId": "x", "state": state}] if present else []
    print(state, "present" if present else "absent", "edited" if edited else "same", "changed" if (outbox and pending["state"]!=state) else "same state", current_clears({"clientId": "x", "sentText": ""}, outbox, "" if not edited else "edited", ""))
PY

echo "== exact diff for current effect around lines 52-61 =="
cat -n "$FILE" | sed -n '48,64p'

Repository: superset-sh/superset

Length of output: 5847


Clear the composer draft only on confirmed sends.

Composer tracks the sent clientId and clears both the textarea and draft localStorage when that entry leaves outbox. outbox can lose an entry for confirmation or discard, and Transcript exposes discard for failed outbox entries. If the user discards a failed prompt without editing the text, this path deletes the user’s message payload. Track the entry through confirmation, or pass a clear-only-on-success signal, and avoid clearing on discard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx
around lines 52 - 61, Update the pending-send handling in Composer’s useEffect
so the textarea and draft localStorage are cleared only after the tracked
clientId is confirmed as successfully sent, not merely removed from outbox.
Distinguish confirmation from discard using the existing entry status or a
clear-only-on-success signal, while preserving the valueRef check and pendingRef
cleanup.

Comment on lines +6 to +20
const MarkdownBlock = memo(function MarkdownBlock({
block,
}: {
block: string;
}) {
return (
<Streamdown
className="[&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
linkSafety={{ enabled: false }}
mode="streaming"
>
{block}
</Streamdown>
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move MarkdownBlock into its own component file.

MarkdownBlock is a separate component. Place it in MarkdownBlock/MarkdownBlock.tsx and add an index.ts barrel export.

As per coding guidelines, “Use one component per file.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/MarkdownView.tsx
around lines 6 - 20, Move the memoized MarkdownBlock component into a new
MarkdownBlock/MarkdownBlock.tsx file, preserving its props, Streamdown
configuration, and rendering behavior. Add an index.ts barrel export for
MarkdownBlock, then update MarkdownView to import and use the extracted
component instead of defining it locally.

Source: Coding guidelines

Comment on lines +45 to +53
<Composer
draftKey={`chat-v3-draft:new:${workspaceId}`}
onSend={(content) => {
onSend(content);
return null;
}}
outbox={[]}
placeholder={`Start a ${harness} session`}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not clear the draft before session creation succeeds.

onSend returns null unconditionally, so Composer clears the textarea and deletes the draft key immediately. ChatV3Pane implements this callback as (content) => void createSession(content), and createSession awaits wiring.transport.createSession without a catch. If session creation fails, three things happen:

  • The typed prompt is already erased and the draft key is already removed.
  • The rejection surfaces only as an unhandled promise rejection.
  • The pane stays on NewSessionView, so the user sees an empty composer and no error.

Change onSend to report success, and keep the draft until creation resolves. Report the failure to the user. Follow the desktop rule that rendered error messages carry select-text cursor-text.

🐛 Proposed direction
-	onSend: (content: UserContent[]) => void;
+	onSend: (content: UserContent[]) => Promise<boolean>;
 			<Composer
 				draftKey={`chat-v3-draft:new:${workspaceId}`}
-				onSend={(content) => {
-					onSend(content);
-					return null;
-				}}
+				onSend={onSend}
 				outbox={[]}
 				placeholder={`Start a ${harness} session`}
 			/>

Composer.send then needs to await the result and clear only when it is true.

Based on learnings from the coding guidelines: "Rendered error messages must include select-text cursor-text classes so users can copy them; Sonner toasts are exempt."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
around lines 45 - 53, Update the NewSessionView Composer onSend flow and its
ChatV3Pane createSession callback to return a success boolean, resolving true
only after session creation succeeds and false on failure. Have Composer.send
await this result and clear the textarea and draft key only when it is true; on
failure, preserve the draft and render a user-visible error with select-text
cursor-text classes, avoiding an unhandled rejection.

Source: Coding guidelines

Comment on lines +22 to +24
<pre className="max-h-80 overflow-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2">
{output}
</pre>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make terminal diagnostics selectable.

A failed command can expose its only diagnostic in this <pre>. Add select-text cursor-text so users can copy the output.

As per coding guidelines, “Rendered error messages must include select-text cursor-text classes.” The tooltip exception does not apply because this is persistent content.

Proposed fix
-			<pre className="max-h-80 overflow-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2">
+			<pre className="max-h-80 cursor-text select-text overflow-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<pre className="max-h-80 overflow-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2">
{output}
</pre>
<pre className="max-h-80 cursor-text select-text overflow-auto whitespace-pre-wrap rounded-md bg-muted/50 p-2">
{output}
</pre>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/TerminalContent.tsx
around lines 22 - 24, Update the <pre> element in TerminalContent to include the
select-text and cursor-text classes alongside its existing classes, allowing
users to select and copy persistent terminal output.

Sources: Coding guidelines, Learnings

Comment on lines +127 to +130
{group.turn?.status === "failed" && (
<div className="text-xs text-destructive">
Turn failed{group.turn.error ? `: ${group.turn.error.message}` : ""}
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make persistent error text selectable.

These persistent error messages do not include select-text cursor-text. Users cannot reliably select and copy them.

  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx#L127-L130: Add select-text cursor-text to the failed-turn message.
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx#L153-L157: Add select-text cursor-text when the outbox state is "failed".
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/NoticeRow.tsx#L4-L15: Add select-text cursor-text when item.noticeKind === "error".

As per coding guidelines, “Rendered error messages must include select-text cursor-text classes so users can copy them.”

📍 Affects 3 files
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx#L127-L130 (this comment)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx#L153-L157
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/NoticeRow.tsx#L4-L15
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx
around lines 127 - 130, Make all persistent error messages selectable by adding
the select-text cursor-text classes to the failed-turn message in
TurnGroupSection.tsx:127-130, the outbox-failed rendering in
Transcript.tsx:153-157, and the error notice rendering in NoticeRow.tsx:4-15;
leave non-error states unchanged.

Source: Coding guidelines

Comment on lines +9 to +40
const cases: Array<[Item, string]> = [
[{ ...base, kind: "user_message", content: [] }, "user_message"],
[{ ...base, kind: "agent_message", text: "" }, "agent_message"],
[{ ...base, kind: "reasoning", text: "" }, "reasoning"],
[
{
...base,
kind: "tool_call",
title: "ls",
toolKind: "execute",
toolName: "Bash",
status: "running",
content: [],
},
"tool_call",
],
[{ ...base, kind: "plan", entries: [] }, "plan"],
[
{
...base,
kind: "approval_request",
targetItemId: null,
title: "Allow?",
status: "pending",
},
"approval_request",
],
[{ ...base, kind: "notice", noticeKind: "info" }, "notice"],
];
for (const [item, expected] of cases) {
expect(rowKindForItem(item)).toBe(expected as never);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Keep the expected row kind type-safe.

expected as never disables the compiler check for expected row kinds. Import RowKind, type cases as Array<[Item, RowKind]>, and pass expected directly to toBe.

As per coding guidelines, “Avoid any unless necessary; preserve type safety across JavaScript/TypeScript code.”

Proposed fix
+import type { RowKind } from "./rowKind";
 
-		const cases: Array<[Item, string]> = [
+		const cases: Array<[Item, RowKind]> = [
 ...
-			expect(rowKindForItem(item)).toBe(expected as never);
+			expect(rowKindForItem(item)).toBe(expected);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cases: Array<[Item, string]> = [
[{ ...base, kind: "user_message", content: [] }, "user_message"],
[{ ...base, kind: "agent_message", text: "" }, "agent_message"],
[{ ...base, kind: "reasoning", text: "" }, "reasoning"],
[
{
...base,
kind: "tool_call",
title: "ls",
toolKind: "execute",
toolName: "Bash",
status: "running",
content: [],
},
"tool_call",
],
[{ ...base, kind: "plan", entries: [] }, "plan"],
[
{
...base,
kind: "approval_request",
targetItemId: null,
title: "Allow?",
status: "pending",
},
"approval_request",
],
[{ ...base, kind: "notice", noticeKind: "info" }, "notice"],
];
for (const [item, expected] of cases) {
expect(rowKindForItem(item)).toBe(expected as never);
}
import type { RowKind } from "./rowKind";
const cases: Array<[Item, RowKind]> = [
[{ ...base, kind: "user_message", content: [] }, "user_message"],
[{ ...base, kind: "agent_message", text: "" }, "agent_message"],
[{ ...base, kind: "reasoning", text: "" }, "reasoning"],
[
{
...base,
kind: "tool_call",
title: "ls",
toolKind: "execute",
toolName: "Bash",
status: "running",
content: [],
},
"tool_call",
],
[{ ...base, kind: "plan", entries: [] }, "plan"],
[
{
...base,
kind: "approval_request",
targetItemId: null,
title: "Allow?",
status: "pending",
},
"approval_request",
],
[{ ...base, kind: "notice", noticeKind: "info" }, "notice"],
];
for (const [item, expected] of cases) {
expect(rowKindForItem(item)).toBe(expected);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.test.ts
around lines 9 - 40, Update the rowKind test cases to import and use the RowKind
type, changing cases to Array<[Item, RowKind]>. Remove the expected as never
cast and pass expected directly to toBe so the compiler validates each expected
row kind.

Source: Coding guidelines

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx (1)

45-52: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not clear the draft before session creation succeeds.

onSend invokes the async callback and always returns null. Composer treats null as success and removes the textarea value and persisted draft immediately. If wiring.transport.createSession rejects, the draft is lost and the rejected promise is unhandled.

Return an acknowledged async result from onSend. Clear the draft only after creation succeeds. On failure, preserve the draft and render a persistent error with select-text cursor-text classes.

Proposed direction
- onSend: (content: UserContent[]) => void;
+ onSend: (content: UserContent[]) => Promise<boolean>;
...
- onSend={(content) => {
-   onSend(content);
-   return null;
- }}
+ onSend={onSend}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
around lines 45 - 52, Update the Composer onSend handler in NewSessionView so it
returns the session-creation promise and acknowledges success only after onSend
completes, allowing failures to preserve the draft instead of being treated as
successful. Handle rejected creation by rendering a persistent error message
with select-text cursor-text classes, while keeping the draft intact.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx:
- Around line 45-52: Update the Composer onSend handler in NewSessionView so it
returns the session-creation promise and acknowledges success only after onSend
completes, allowing failures to preserve the draft instead of being treated as
successful. Handle rejected creation by rendering a persistent error message
with select-text cursor-text classes, while keeping the draft intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 425284ba-f67d-47be-9624-868c592e4094

📥 Commits

Reviewing files that changed from the base of the PR and between af10c7b and 0d3bcd0.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (57)
  • apps/desktop/package.json
  • apps/desktop/src/renderer/lib/persisted-keys/persisted-key-registry.test-data.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/MarkdownView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/NewSessionView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/SessionHeader.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/SessionPicker.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/AgentMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/ApprovalRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/NoticeRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/PlanRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/ReasoningRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/ToolContentList.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/DiffContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/TerminalContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/UnknownItemRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/UserMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types.ts
  • apps/desktop/tsconfig.json
  • packages/chat/src/client/subscribeToSession/subscribeToSession.test.ts
  • packages/chat/src/react/useChatSession/useChatSession.ts
🚧 Files skipped from review as they are similar to previous changes (55)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/index.ts
  • apps/desktop/package.json
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/TerminalContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UserMessageRow/UserMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/utils/rowKind/rowKind.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/index.ts
  • apps/desktop/src/renderer/lib/persisted-keys/persisted-key-registry.test-data.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TerminalContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/TextContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/NewSessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/index.ts
  • packages/chat/src/client/subscribeToSession/subscribeToSession.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/MarkdownView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/TextContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionHeader/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/ToolCallRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/components/DiffContent/DiffContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionView/SessionView.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/ChatV3Pane.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolCallRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/AgentMessageRow/AgentMessageRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/index.ts
  • packages/chat/src/react/useChatSession/useChatSession.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/SessionPicker/SessionPicker.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/NoticeRow/NoticeRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/hooks/useSessionClient/useSessionClient.ts
  • apps/desktop/tsconfig.json
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ApprovalRow/ApprovalRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ToolContentList/ToolContentList.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/TurnGroupSection/TurnGroupSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/Transcript.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/PlanRow/PlanRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Composer/Composer.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/ReasoningRow/ReasoningRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/Transcript/components/UnknownItemRow/UnknownItemRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatV3Pane/components/MarkdownView/utils/planMarkdown/planMarkdown.ts

This branch was previously deployed

1 inactive deployment
preview — 0d3bcd0d Deployed Aug 5, 2026 by saddlepaddle via Deploy Marketing #7180
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant